package com.pig4cloud.pig.common.core.util;


import java.math.BigDecimal;

/**
 * 浮点数保留精度计算工具类，用于浮点数的加、减、乘、除、四舍五入、向上/向下取数等运算
 **/
public class BigDecimalUtils {

	/**
	 * BigDeciaml精确除法默认保留小数点后位数
	 */
	public static final int DEF_SCALE = 2;

    /**
     * 精确的除法运算，默认保留小数点后两位，以后的数字四舍五入。
     * 因为存在保留小数点位数后四舍五入，并不能保证完全的精确计算。<br>
     * 另外，禁止float类型的入参，float转double类型会存在精度误差。<br>
     * 使用例子： BigDecimalUtils.divide(0.1, 0.8); 结果为 0.13
     * @param d1 被除数
     * @param d2 除数
     * @return 除法运算结果
     */
    public static double divide(double d1, double d2){
        return divide(d1,d2, DEF_SCALE);
    }

    /**
     * 精确的除法运算，保留小数位后scale位，以后的数字四舍五入。
     * 可以通过scale参数尽可能的保证计算结果精确。<br>
     * 另外，禁止传入参与除法运算的float类型参数，float转double类型会存在精度误差。<br>
     * 使用例子： BigDecimalUtils.divide(0.1, 0.7, 3); 结果为 0.145
     * @param d1 被除数
     * @param d2 除数
     * @param scale 小数点后保留位数
     * @return 除法运算结果小数点后保留scale位
     */
    public static double divide(double d1, double d2, int scale) {
        if(scale < 0){
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(d1));
        BigDecimal b2 = new BigDecimal(Double.toString(d2));
        BigDecimal b3 = b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP);
        return b3.doubleValue();
    }

    /**
     * 精确的加法运算。<br>
     * 禁止float类型的入参，float转double类型会存在精度误差。<br>
     * 使用例子：BigDecimalUtils.add(0.01, 2.5); 结果为2.51
     * @param v1 被加数
     * @param v2 加数
     * @return 两数之和
     */
    public static double add(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    /**
     * 精确的减法运算。<br>
     * 禁止float类型的入参，float转double类型会存在精度误差。<br>
     * 使用例子：BigDecimalUtils.sub(0.05,0.02); 结果为0.03
     * @param d1 被减数
     * @param d2 减数
     * @return 两数之差
     */
    public static double sub(double d1, double d2) {
        BigDecimal b1 = new BigDecimal(Double.toString(d1));
        BigDecimal b2 = new BigDecimal(Double.toString(d2));
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 精确的乘法运算。<br>
     * 禁止float类型的入参，float转double类型会存在精度误差。<br>
     * 使用例子：BigDecimalUtils.mul(0.1, 0.05); 结果为0.005
     * @param d1 被乘数
     * @param d2 乘数
     * @return 两数之积
     */
    public static double mul(double d1, double d2) {
        BigDecimal b1 = new BigDecimal(Double.toString(d1));
        BigDecimal b2 = new BigDecimal(Double.toString(d2));
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 小数位四舍五入处理。<br>
     * 使用例子：BigDecimalUtils.round(1.125, 2); 结果为 1.13
     * @param num 需要四舍五入的数字 BigDecimal类型
     * @param scale 小数点后保留几位
     * @return 返回四舍五入的数据
     */
    public static double round(double num, int scale) {
        BigDecimal b = new BigDecimal(Double.toString(num));
        return round(b,scale).doubleValue();
    }

    /**
     * 精确的小数位四舍五入处理。<br>
     * 使用例子：BigDecimalUtils.round(new BigDecimal("1.125"), 2);
     * @param num 需要四舍五入BigDecimal类型的数字
     * @param scale 小数点后保留几位
     * @return 返回四舍五入的BigDecimal类型
     */
    public static BigDecimal round(BigDecimal num, int scale) {
        if(scale < 0){
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        return num == null ? null : num.setScale(scale,BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 根据保留小数位数向上取数。<br>
     * 禁止float类型的入参，float转double类型会存在精度误差。<br>
     * 使用例子：BigDecimalUtils.ceil(1.125, 2) 结果为 1.13
     * @param num 指定数字
     * @param scale 保留小数位数
     * @return 返回保留小数位数向上取的数据
     */
    public static double ceil(double num,int scale){
        BigDecimal b = new BigDecimal(Double.toString(num));
        return ceil(b,scale).doubleValue();
    }

    /**
     * 根据保留小数位数向上取数。<br>
     * 使用例子：BigDecimalUtils.ceil(new BigDecimal("1.125"), 2);
     * @param num 指定数字 BigDecimal类型
     * @param scale 保留小数位数
     * @return 返回保留小数位数向上取的BigDecimal类型
     */
    public static BigDecimal ceil(BigDecimal num, int scale) {
        if(scale < 0){
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        return num == null ? null : num.setScale(scale,BigDecimal.ROUND_UP);
    }

    /**
     * 根据保留小数位数向下取数。<br>
     * 禁止float类型的入参，float转double类型会存在精度误差。<br>
     * 使用例子：BigDecimalUtils.floor(1.125, 2) 结果为 1.12
     * @param num 指定数字
     * @param scale 保留小数位数
     * @return 返回保留小数位数向下取的数据
     */
    public static double floor(double num,int scale){
        BigDecimal b = new BigDecimal(Double.toString(num));
        return floor(b,scale).doubleValue();
    }

    /**
     * 根据保留小数位数向下取数。<br>
     * 使用例子：BigDecimalUtils.floor(new BigDecimal("1.125"), 2);
     * @param num 需要四舍五入的数字 BigDecimal类型
     * @param scale 保留小数位数
     * @return 返回保留小数位数向下取的BigDecimal类型
     */
    public static BigDecimal floor(BigDecimal num, int scale) {
        if(scale < 0){
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        return num == null ? null : num.setScale(scale,BigDecimal.ROUND_DOWN);
    }

}